﻿#region 命名空间

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;

#endregion

namespace Sharp.Infrastructure
{
    /// <summary>
    ///     序列化、反序列化帮助类
    /// </summary>
    public class SerializableHelper
    {
        private static readonly JsonSerializerSettings JsonSettings;
        private static readonly List<string> Errors = new List<string>();

        static SerializableHelper()
        {
            JsonSettings = new JsonSerializerSettings
            {
                ContractResolver = new CamelCasePropertyNamesContractResolver(),
                MissingMemberHandling = Newtonsoft.Json.MissingMemberHandling.Ignore,
                NullValueHandling = Newtonsoft.Json.NullValueHandling.Include,
                ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore,
                Error = delegate (object obj, Newtonsoft.Json.Serialization.ErrorEventArgs args)
                {
                    Errors.Add(args.ErrorContext.Error.Message);
                    args.ErrorContext.Handled = true;
                }
            };
            //解决时间毫秒精度太高，ie9或更低浏览器无法识别。
            IsoDateTimeConverter datetimeConverter = new IsoDateTimeConverter
            {
                DateTimeFormat = "yyyy/MM/dd HH:mm:ss"
            };
            JsonSettings.Converters.Add(datetimeConverter);
        }

        /// <summary>
        ///     序列化成一个字节数组
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        public static byte[] SerializeToBytes<T>(T t)
        {
            using (MemoryStream mStream = new MemoryStream())
            {
                BinaryFormatter ser = new BinaryFormatter();
                ser.Serialize(mStream, t);
                return mStream.ToArray();
            }
        }

        /// <summary>
        ///     序列化成一个字符串
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <param name="df"></param>
        /// <returns>序列化代码</returns>
        public static string SerializeToString<T>(T t, DataFormart df)
        {
            if (df == DataFormart.Xml)
            {
                XmlSerializer s = new XmlSerializer(typeof(T));
                using (Stream stream = new MemoryStream())
                {
                    s.Serialize(stream, t);

                    stream.Seek(0, SeekOrigin.Begin);

                    StreamReader reader = new StreamReader(stream);

                    return reader.ReadToEnd();
                }
            }
            else
            {
                return JsonConvert.SerializeObject(t, JsonSettings);
            }
        }

        /// <summary>
        ///     序列化泛型数组为xml文件
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <param name="fullName"></param>
        /// <returns>是否序列化成功</returns>
        public static bool Serialize<T>(T t, string fullName)
        {
            XmlSerializer xmlSerializer = new XmlSerializer(t.GetType());
            TextWriter writer = new StreamWriter(fullName);
            try
            {
                xmlSerializer.Serialize(writer, t);

                return true;
            }
            catch
            {
                return false;
            }
            finally
            {
                writer.Close();
            }
        }

        /// <summary>
        ///     序列化 Dictionary
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="dic"></param>
        /// <param name="fullName"></param>
        /// <returns></returns>
        public static bool Serialize<TKey, TValue>(Dictionary<TKey, TValue> dic, string fullName)
        {
            try
            {
                XmlWriterSettings settings = new XmlWriterSettings
                {
                    Encoding = Encoding.UTF8,
                    Indent = true,
                    ConformanceLevel = ConformanceLevel.Fragment
                };

                using (XmlWriter writer = XmlWriter.Create(fullName, settings))
                {
                    XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
                    XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));


                    writer.WriteStartElement("Root");
                    foreach (KeyValuePair<TKey, TValue> kv in dic)
                    {
                        writer.WriteStartElement("Row");
                        writer.WriteStartElement("Key");
                        keySerializer.Serialize(writer, kv.Key);
                        writer.WriteEndElement();
                        writer.WriteStartElement("Value");
                        valueSerializer.Serialize(writer, kv.Value);
                        writer.WriteEndElement();
                        writer.WriteEndElement();
                    }
                    writer.WriteEndElement();


                    return true;
                }
            }
            catch
            {
                try
                {
                    File.Delete(fullName);
                }
                catch
                {
                }
                return false;
            }
        }


        /// <summary>
        ///     xml 文件反序列化为泛型数组
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="fi"></param>
        /// <returns></returns>
        public static T DeSerialize<T>(FileInfo fi)
        {
            return DeSerialize<T>(fi, DataFormart.Xml);
        }

        /// <summary>
        ///     反序列化
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="fi"></param>
        /// <param name="df"></param>
        /// <returns></returns>
        public static T DeSerialize<T>(FileInfo fi, DataFormart df)
        {
            if (fi.Exists == false)
                return default(T);
            if (df == DataFormart.Xml)
            {
                XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));

                FileStream fs = fi.OpenRead();
                T t;
                try
                {
                    t = (T)xmlSerializer.Deserialize(fs);
                }
                catch (Exception ex)
                {
                    throw new Exception(ex.Message + "\r\n" + fi.FullName);
                }
                finally
                {
                    fs.Close();
                }
                return t;
            }

            else if (df == DataFormart.Binary)
            {
                BinaryFormatter fmt = new BinaryFormatter();
                using (FileStream fsReader = fi.OpenRead())
                {
                    T t = (T)fmt.Deserialize(fsReader);

                    return t;
                }
            }
            else
            {
                DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));

                using (FileStream stream = fi.OpenRead())
                {
                    Byte[] buffer = new Byte[stream.Length];
                    stream.Read(buffer, 0, Convert.ToInt32(stream.Length));

                    using (MemoryStream mStream = new MemoryStream(buffer))
                    {
                        return (T)serializer.ReadObject(mStream);
                    }
                }
            }
        }

        /// <summary>
        ///     反序列化
        /// </summary>
        /// <param name="fi"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public static object DeSerialize(FileInfo fi, Type type)
        {
            if (fi.Exists == false)
                return null;

            XmlSerializer xmlSerializer = new XmlSerializer(type);

            FileStream fs = fi.OpenRead();
            object obj;
            try
            {
                obj = xmlSerializer.Deserialize(fs);
            }
            finally
            {
                fs.Close();
            }
            return obj;
        }

        /// <summary>
        ///     反序列化
        /// </summary>
        /// <param name="stringSource"></param>
        /// <param name="type"></param>
        /// <param name="df"></param>
        /// <returns></returns>
        public static object DeSerialize(string stringSource, Type type, DataFormart df)
        {
            if (df == DataFormart.Binary)
            {
                throw new NotSupportedException("字符串没法反串行化");
            }
            if (string.IsNullOrEmpty(stringSource))
                return null;

            if (df == DataFormart.Xml)
            {
                XmlSerializer x = new XmlSerializer(type);

                using (Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(stringSource)))
                {
                    stream.Seek(0, SeekOrigin.Begin);
                    object obj = x.Deserialize(stream);

                    return obj;
                }
            }
            else
            {
                return JsonConvert.DeserializeObject(stringSource, type, JsonSettings);
            }
        }

        /// <summary>
        ///     字符串反序列化成一个类
        /// </summary>
        /// <param name="stringSource"></param>
        /// <param name="df"></param>
        /// <returns></returns>
        public static T DeSerialize<T>(string stringSource, DataFormart df)
        {
            if (string.IsNullOrEmpty(stringSource)) return default(T);

            if (df == DataFormart.Binary)
            {
                throw new NotSupportedException("字符串没法反串行化");
            }
            if (string.IsNullOrEmpty(stringSource))
                return default(T);

            if (df == DataFormart.Xml)
            {
                XmlSerializer x = new XmlSerializer(typeof(T));

                using (Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(stringSource)))
                {
                    stream.Seek(0, SeekOrigin.Begin);
                    object obj = x.Deserialize(stream);

                    return (T)obj;
                }
            }
            else
            {
                return JsonConvert.DeserializeObject<T>(stringSource, JsonSettings);
            }
        }

        /// <summary>
        ///     从Dictionary类反序列化
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="fi"></param>
        /// <returns></returns>
        public static Dictionary<TKey, TValue> DeSerialize<TKey, TValue>(FileInfo fi)
        {
            if (fi.Exists == false)
                return default(Dictionary<TKey, TValue>);

            FileStream fs = fi.OpenRead();
            if (fs.Length == 0)
                return default(Dictionary<TKey, TValue>);

            XmlReader reader = XmlReader.Create(fs);
            XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
            XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));
            bool wasEmpty = reader.IsEmptyElement;

            if (wasEmpty)
                return default(Dictionary<TKey, TValue>);

            Dictionary<TKey, TValue> dic = new Dictionary<TKey, TValue>();
            while (reader.Read())
            {
                if (reader.NodeType != XmlNodeType.Element) continue;
                if (reader.Name == "Root") continue;

                reader.ReadStartElement("Row");
                reader.ReadStartElement("Key");
                TKey key = (TKey)keySerializer.Deserialize(reader);
                reader.ReadEndElement();
                reader.ReadStartElement("Value");
                TValue value = (TValue)valueSerializer.Deserialize(reader);
                reader.ReadEndElement();
                dic.Add(key, value);
                reader.ReadEndElement();
            }
            fs.Close();
            return dic;
        }

        private static bool IsAnonymousType(Type type)
        {
            if (!type.IsGenericType)
                return false;

            if ((type.Attributes & TypeAttributes.NotPublic) != TypeAttributes.NotPublic)
                return false;

            if (!Attribute.IsDefined(type, typeof(CompilerGeneratedAttribute), false))
                return false;

            return type.Name.Contains("AnonymousType");
        }
    }

    /// <summary>
    ///     数据格式
    /// </summary>
    public enum DataFormart
    {
        /// <summary>
        ///     二进制
        /// </summary>
        Binary,

        /// <summary>
        ///     Xml格式
        /// </summary>
        Xml,

        /// <summary>
        ///     Json格式
        /// </summary>
        Json
    }
}